<?php

namespace App\Http\Controllers\AgentAdmin;

use App\Enums\AssetStatus;
use App\Enums\ExchangeChannel;
use App\Enums\ProductStatus;
use App\Enums\ProductType;
use App\Enums\RightsType;
use App\Exports\ProductExport;
use App\Http\Controllers\Controller;
use App\Jobs\DownloadsJob;
use App\Models\AgentUser;
use App\Models\Asset;
use App\Models\Download;
use App\Models\Product;
use App\Models\ProductCategory;
use App\Models\ProductConfig;
use App\Models\ProductToken;
use App\Models\RightsEntityProduct;
use App\Services\ObsService;
use App\Services\ProductService;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Vinlon\Laravel\LayAdmin\Exceptions\AdminException;

class ProductController extends Controller
{
    /** @var ProductService */
    private $productService;

    /** @var ObsService */
    private $obsService;

    /**
     * ProductController constructor.
     */
    public function __construct(ProductService $productService, ObsService $obsService)
    {
        $this->productService = $productService;
        $this->obsService = $obsService;
    }

    /** 以树形结构返回所有分类数据 */
    public function categoryTree()
    {
        /** @var AgentUser $user */
        $user = auth()->user();

        return $this->productService->getCategoryTree($user->agent_id);
    }

    /** 保存类别数据 */
    public function saveCategories()
    {
        request()->validate([
            'id' => 'array',
            'name' => 'array',
            'is_parent' => 'array',
        ]);

        \DB::transaction(function () {
            $idList = request()->id ?: [];
            $nameList = request()->name ?: [];
            $isParentList = request()->is_parent ?: [];

            /** @var AgentUser $user */
            $user = auth()->user();

            // 先删除不存在的ID
            ProductCategory::query()
                ->where('agent_id', $user->agent_id)
                ->whereNotIn('id', $idList)->delete();

            $parentSequence = 0;
            $childSequence = 0;
            $parentId = 0;
            foreach ($nameList as $index => $name) {
                $isParent = Arr::get($isParentList, $index, true);
                $id = Arr::get($idList, $index);
                $category = ProductCategory::findOrNew($id);
                $category->agent_id = $user->agent_id;
                if ($isParent) {
                    $category->name = $name;
                    $category->parent_id = 0;
                    $category->sequence = $parentSequence;
                    $category->save();
                    $parentId = $category->id;
                    ++$parentSequence;
                    $childSequence = 0;
                } else {
                    $category->name = $name;
                    $category->parent_id = $parentId;
                    $category->sequence = $childSequence;
                    $category->save();
                }
            }
        });
    }

    /** 查询发行商品列表 */
    public function listProducts()
    {
        $query = $this->getAgentProductQuery()
            ->when(request()->keyword, function ($q) {
                $likeVal = '%' . request()->keyword . '%';

                return $q->where('name', 'like', $likeVal);
            })
            ->with(['configs', 'store', 'entity'])
            ->withCount(['available_tokens'])
            ->where('type', ProductType::ISSUE)
            ->orderByDesc('sequence')
            ->orderByDesc('id');

        return paginate_result($query, function (Product $product) {
            $arr = $product->toArray();
            $arr['configs'] = $product->configs->mapWithKeys(function (ProductConfig $config) {
                return [$config->config_key => $config->config_value];
            });

            return $arr;
        });
    }

    /** 查询转售商品列表 */
    public function listResaleProducts()
    {
        $query = $this->getAgentProductQuery()
            ->when(request()->keyword, function ($q) {
                $likeVal = '%' . request()->keyword . '%';

                return $q->where('name', 'like', $likeVal);
            })
            ->when(request()->min_price, function ($q) {
                return $q->where('price', '>=', toFen(request()->min_price));
            })
            ->when(request()->max_price, function ($q) {
                return $q->where('price', '<=', toFen(request()->max_price));
            })
            ->with(['resale_user.auth'])
            ->where('type', ProductType::RESALE)
            ->orderByDesc('id');

        return paginate_result($query);
    }

    /** 查询肓盒商品列表 */
    public function listBlindBoxes()
    {
        $query = $this->getAgentProductQuery()
            ->with(['configs'])
            ->when(request()->keyword, function ($q) {
                $likeVal = '%' . request()->keyword . '%';

                return $q->where('name', 'like', $likeVal);
            })
            ->where('type', ProductType::BLIND_BOX)
            ->orderByDesc('sequence')
            ->orderByDesc('id');

        return paginate_result($query, function (Product $product) {
            $arr = $product->toArray();
            $arr['configs'] = $product->configs->mapWithKeys(function (ProductConfig $config) {
                return [$config->config_key => $config->config_value];
            });

            return $arr;
        });
    }

    /** 查询商品详情 */
    public function getProduct($id)
    {
        $product = Product::query()
            ->with(['store', 'resale_user', 'category', 'configs'])
            ->find($id);
        $arr = $product->toArray();
        $arr['configs'] = $product->configs->mapWithKeys(function (ProductConfig $config) {
            return [$config->config_key => $config->config_value];
        });

        return $arr;
    }

    /** 查询商品存证列表 */
    public function listProductTokens($id)
    {
        $query = ProductToken::query()
            ->where('product_id', $id)
            ->orderBy('id');

        return paginate_result($query);
    }

    /** 上传商品图片 */
    public function uploadProductImage()
    {
        request()->validate([
            'file' => ['image', 'mimes:jpg,jpeg,png,gif', 'max:20480'],
        ], [
            'file.max' => '图片尺寸不能超过2M',
            'file.mimes' => '图片格式必须为jpg/jpeg/png/gif',
        ]);
        $key = 'product_image/' . uniqid();
        $file = request()->file('file');
        $imageUrl = $this->obsService->upload($key, $file);

        return [
            'image_url' => $imageUrl,
        ];
    }

    /** 商品编辑 */
    public function editProduct($id)
    {
        /** @var AgentUser $user */
        $user = auth()->user();

        request()->validate([
            'name' => 'required',
            'store_id' => 'required',
            'category_id' => 'required',
            'logo_url' => 'required',
            'asset_image_url' => 'nullable',
            'sequence' => 'nullable | integer',
            'author' => 'nullable',
            'issuer' => 'nullable',
            'hold_rights' => 'nullable',
            'intro' => 'nullable',
            'description' => 'nullable',
        ]);

        $product = $this->getAgentProductQuery()->find($id);

        Product::query()
            ->where('contract_address', $product->contract_address)
            ->update([
                'name' => request()->name,
                'store_id' => request()->store_id,
                'category_id' => request()->category_id,
                'logo_url' => request()->logo_url,
                'asset_image_url' => request()->asset_image_url ?: '',
                'author' => request()->author ?: '',
                'issuer' => request()->issuer ?: '',
                'hold_rights' => request()->hold_rights ?: '',
                'intro' => request()->intro ?: '',
                'description' => request()->description ?: '',
                'agent_id' => $user->agent_id,
                'sequence' => request()->sequence ?: 0,
            ]);
    }

    /** 保存转售配置 */
    public function saveResaleConfig($id)
    {
        $params = request()->validate([
            ProductConfig::RESALE_FORBID => 'required | boolean',
            ProductConfig::RESALE_DURATION => 'required | integer',
            ProductConfig::RESALE_MAX_PRICE => 'required | integer',
            ProductConfig::RESALE_PRICE_INCR_LIMIT => 'required | integer',
            ProductConfig::RESALE_PRICE_DECR_LIMIT => 'required | integer',
            ProductConfig::RESALE_INIT_BASE_PRICE => 'required',
            ProductConfig::GIVE_FORBID => 'required | boolean',
            ProductConfig::GIVE_DURATION => 'required | integer',
        ]);
        $product = Product::query()->find($id);

        ProductConfig::query()->where('contract_address', $product->contract_address)
            ->whereIn('config_key', [
                ProductConfig::RESALE_FORBID,
                ProductConfig::RESALE_DURATION,
                ProductConfig::RESALE_MAX_PRICE,
                ProductConfig::RESALE_PRICE_INCR_LIMIT,
                ProductConfig::RESALE_PRICE_DECR_LIMIT,
                ProductConfig::RESALE_INIT_BASE_PRICE,
                ProductConfig::GIVE_FORBID,
                ProductConfig::GIVE_DURATION,
            ])->delete();

        foreach ($params as $key => $value) {
            $config = new ProductConfig();
            $config->contract_address = $product->contract_address;
            $config->config_key = $key;
            $config->config_value = $value;
            $config->save();
        }

        // 如果是禁止转售，则将已经转售的资产下架
        if (request()->boolean(ProductConfig::RESALE_FORBID)) {
            // 查询当前商品的所有转售中的商品
            $resaleProductIds = Product::query()
                ->select(['id'])
                ->where('type', ProductType::RESALE)
                ->where('contract_address', $product->contract_address)
                ->where('left_count', '>', 0)
                ->where('status', ProductStatus::ON_SALE)
                ->get()->map(function (Product $product) {
                    return $product->id;
                });
            // 商品标记为下架
            Product::query()->whereIn('id', $resaleProductIds)
                ->update(['status' => ProductStatus::STOP]);

            $tokenIds = ProductToken::query()
                ->whereIn('product_id', $resaleProductIds)
                ->get()->map(function (ProductTOken $token) {
                    return $token->token_id;
                });

            // 资产恢复为持有状态
            Asset::query()->where('asset_status', AssetStatus::ON_SALE)
                ->where('contract_address', $product->contract_address)
                ->whereIn('token_id', $tokenIds)
                ->update(['asset_status' => AssetStatus::HOLD]);
        }
    }

    /** 商品发布 */
    public function releaseProduct($id)
    {
        /** @var AgentUser $user */
        $user = auth()->user();
        request()->validate([
            'store_id' => 'required',
            'display_price' => 'required',
            'buy_limit' => 'required',
            'start_time' => 'nullable',
            'end_time' => 'nullable',
            'left_count' => 'required',
            'show_left_count' => 'required | boolean',
        ]);

        /** @var Product $product */
        $product = $this->getAgentProductQuery()->find($id);

        $isLocked = $product->configs()->where('config_key', ProductConfig::IS_LOCKED)->value('config_value');
        $leftCount = request()->left_count;
        if ($isLocked && 0 != $leftCount) {
            throw new AdminException('锁定资产可以上架展示，但库存不能大于0');
        }
        $maxInventory = $product->available_tokens()->count();
        if ($leftCount > $maxInventory) {
            throw new AdminException('库存不能超过' . $maxInventory);
        }

        $product->store_id = request()->store_id;
        $product->price = toFen(request()->display_price);
        $product->left_count = $leftCount;
        $product->status = ProductStatus::ON_SALE;
        $product->agent_id = $user->agent_id;
        $product->online_time = now();
        $product->start_time = request()->start_time ?: now();
        $product->end_time = request()->end_time;
        $product->buy_limit = request()->buy_limit;
        $product->save();

        //保存是否显示实时库存的配置
        ProductConfig::set($product->contract_address, ProductConfig::SHOW_LEFT_COUNT, request()->show_left_count);
    }

    /** 商品下架 */
    public function offlineProduct($id)
    {
        $product = $this->getAgentProductQuery()->find($id);
        $product->status = ProductStatus::PAUSE;
        $product->save();
    }

    /** 导出资产持有人有持有数量 */
    public function exportProductHolders()
    {
        $product = Product::query()->find(request()->product_id ?: 0);

        $assets = Asset::query()
            ->select([
                'user_id',
                \DB::raw('count(*) as hold_count'),
            ])
            ->where('contract_address', $product->contract_address)
            ->whereIn('asset_status', [AssetStatus::HOLD, AssetStatus::ON_SALE])
            ->whereHas('user')
            ->groupBy(['user_id'])
            ->with(['user'])
            ->get();

        $fileName = sprintf(
            '【%s】持有记录导出_%s.csv',
            $product->name,
            now()->format('YmdHis')
        );
        $dir = storage_path('app/export/' . today()->format('Ymd'));
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        $path = $dir . '/' . $fileName;
        $file = fopen($path, 'w');
        fwrite($file, chr(0xEF) . chr(0xBB) . chr(0xBF));
        $header = ['资产标识', '商品名称', '持有人', '持有数量'];
        fputcsv($file, $header);
        foreach ($assets as $asset) {
            $row = [
                $product->contract_address,
                $product->name,
                $asset->user->mobile,
                $asset->hold_count,
            ];
            fputcsv($file, $row);
        }
        fclose($file);

        return response()->download($path, $fileName, [
            'Content-Type' => 'text/csv',
        ]);
    }

    /** 导出资产存证列表及持有人 */
    public function exportProductTokens()
    {
        $product = Product::query()->find(request()->product_id ?: 0);

        $assets = Asset::query()
            ->select(['user_id', 'token_id', 'cert_sn', 'created_at', 'asset_status'])
            ->where('contract_address', $product->contract_address)
            ->whereIn('asset_status', [AssetStatus::HOLD, AssetStatus::ON_SALE])
            ->whereHas('user')
            ->with(['user'])
            ->get();

        $fileName = sprintf(
            '【%s】存证记录导出_%s.csv',
            $product->name,
            now()->format('YmdHis')
        );
        $dir = storage_path('app/export/' . today()->format('Ymd'));
        if (!is_dir($dir)) {
            mkdir($dir, 0755, true);
        }
        $path = $dir . '/' . $fileName;
        $file = fopen($path, 'w');
        fwrite($file, chr(0xEF) . chr(0xBB) . chr(0xBF));
        $header = ['资产标识', '商品名称', '存证ID', '证书编号', '持有人', '存证时间', '持有状态'];
        fputcsv($file, $header);
        foreach ($assets as $asset) {
            $row = [
                $product->contract_address,
                $product->name,
                '[token_id]' . $asset->token_id,
                $asset->cert_sn,
                $asset->user->mobile,
                $asset->created_at->toDateTimeString(),
                $asset->asset_status->description,
            ];
            fputcsv($file, $row);
        }
        fclose($file);

        return response()->download($path, $fileName, [
            'Content-Type' => 'text/csv',
        ]);
    }

    /** 保存交易所设置 */
    public function saveExchangeConfig($id)
    {
        request()->validate([
            'is_locked' => 'required | boolean',
            'exchange_channel' => 'required | enum_value:' . ExchangeChannel::class,
        ]);
        $product = Product::query()->find($id);
        if ($product->type->isNot(ProductType::ISSUE())) {
            throw new AdminException('不是发行商品');
        }
        if (!$product->status->in([ProductStatus::PAUSE(), ProductStatus::STOP()])) {
            throw new AdminException('商品不是下架状态');
        }
        $isLocked = request()->is_locked;
        if ($isLocked) {
            $configs = $product->configs->mapWithKeys(function (ProductConfig $config) {
                return [$config->config_key => $config->config_value];
            })->toArray();
            $resaleForbid = Arr::get($configs, ProductConfig::RESALE_FORBID, true);
            if (!$resaleForbid) {
                throw new AdminException('锁定前需要先禁止商品转售');
            }
            $giveForbid = Arr::get($configs, ProductConfig::GIVE_FORBID, true);
            if (!$giveForbid) {
                throw new AdminException('锁定前需要先禁止商品转赠');
            }
        }

        ProductConfig::set($product->contract_address, ProductConfig::IS_LOCKED, $isLocked);
        ProductConfig::set($product->contract_address, ProductConfig::EXCHANGE_CHANNEL, request()->exchange_channel);
    }

    public function assetKeyset($id)
    {
        request()->validate([
            'rights_type' => 'required',
        ]);

        DB::transaction(function () use ($id) {
            $product = Product::query()->find($id);
            $product->rights_type = request()->rights_type;
            $product->save();

            RightsEntityProduct::query()->where('product_id', $id)->delete();

            if (request()->entity_id && RightsType::ENTITY == $product->rights_type) {
                $rightsEntityProduct = new RightsEntityProduct();
                $rightsEntityProduct->entity_id = request()->entity_id;
                $rightsEntityProduct->product_id = $id;
                $rightsEntityProduct->save();
            }
        });
    }

    public function export()
    {
        $user = auth()->user();

        $file = '发行商品' . date('YmdHis') . uniqid() . '.xls';

        $download = Download::create([
            'agent_id' => $user->agent_id,
            'user_id' => $user->id,
            'name' => $file,
            'url' => 'storage/' . $file,
            'status' => 0,
            'model' => ProductExport::class,
            'request' => json_encode(request()->all()),
        ]);

        dispatch(new DownloadsJob($download));
    }

    private function getAgentProductQuery()
    {
        /** @var AgentUser $user */
        $user = auth()->user();

        return $this->productService->getAgentProductQuery($user->agent_id);
    }
}
